package com.mindata.blockchain.socket.common.queue.base;

import com.mindata.blockchain.ApplicationContextProvider;
import com.mindata.blockchain.block.block.Block;
import com.mindata.blockchain.block.instruction.Instruction;
import com.mindata.blockchain.common.Commonvalue;
import com.mindata.blockchain.common.timer.TimerManager;
import com.mindata.blockchain.core.manager.DbBlockManager;
import com.mindata.blockchain.socket.common.event.TransactionMsgEvent;
import com.mindata.blockchain.socket.common.msg.BaseMsg;
import com.mindata.blockchain.socket.common.msg.InstructionMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public abstract class AbstractInstructionsMsgQueue extends BaseMsgQueue {
    public Commonvalue commonValue = ApplicationContextProvider.getBean(Commonvalue.class);
    public DbBlockManager dbBlockManager = ApplicationContextProvider.getBean(DbBlockManager.class);
    /**
     * 存储所有的hash的TX集合
     */
    public ConcurrentHashMap<String, Instruction> txMsgConcurrentHashMap = new ConcurrentHashMap<>();
    /**
     * 存储本节点已签名状态的hash的集合，即本节点已对外广播的交易消息
     */
    public ConcurrentHashMap<String, Boolean> txStateConcurrentHashMap = new ConcurrentHashMap<>();

    public ConcurrentLinkedQueue<Instruction> instructionConcurrentLinkedQueue = new ConcurrentLinkedQueue<>();
    @Resource
    private ApplicationEventPublisher eventPublisher;
    private Logger logger = LoggerFactory.getLogger(AbstractInstructionsMsgQueue.class);

    public abstract void deal();

    @Override
    public void push(BaseMsg msg) {
        List<Instruction> instructions = ((InstructionMsg) msg).getInstructions();
        List<Instruction> UnHandledinstructions = new ArrayList<>();
        for (Instruction instruction : instructions) {
            String hash = instruction.getHash();
            if (txMsgConcurrentHashMap.containsKey(hash)
                    || txStateConcurrentHashMap.containsKey(hash)) {
                continue;
            }
            txMsgConcurrentHashMap.put(hash, instruction);
            UnHandledinstructions.add(instruction);
            txInque(instruction);
        }
        if (!UnHandledinstructions.isEmpty()) {
            int concensusindex = (dbBlockManager.getLastDGLBlockNumber() + 1) % commonValue.getGroupsize();
//            logger.warn("共识序列号为{},节点序号为{}", concensusindex, commonValue.getNodeIndex());
            if (concensusindex != commonValue.getNodeIndex()) {
                ((InstructionMsg) msg).setInstructions(UnHandledinstructions);
                eventPublisher.publishEvent(new TransactionMsgEvent(((InstructionMsg) msg)));
                return;
            }
        } else {
            return;
        }
        deal();
    }

    /**
     * 清理已经提交的instruction的hash
     */
    public void clearOldInstructionHash(Block block) {
        List<Instruction> instructions = block.getBlockBody().getInstructions();
        TimerManager.schedule(() -> {
            for (Instruction temp : instructions) {
                if (txMsgConcurrentHashMap.containsKey(temp.getHash())) {
                    txMsgConcurrentHashMap.remove(temp.getHash());
                    txStateConcurrentHashMap.remove(temp.getHash());
                    // TODO
                    /**
                     * 移除提交到区块链中但还存在于交易池中的交易
                     */
                }
            }
            return null;
        }, 2000);
    }

    public void txInque(Instruction ins) {
        String hash="";
        if (!instructionConcurrentLinkedQueue.isEmpty())
            hash = instructionConcurrentLinkedQueue.peek().getHash();
        if (txStateConcurrentHashMap.containsKey(hash)) {
            instructionConcurrentLinkedQueue.poll();
        }
        instructionConcurrentLinkedQueue.offer(ins);
    }

    public boolean isExist(Object obj) {
        return (txMsgConcurrentHashMap.containsKey(obj));
    }
}
